package de.dopichaj.labrador.search.merge.pattern.test;

import java.io.File;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.SortedSet;
import java.util.TreeSet;

import de.dopichaj.labrador.search.hit.Hit;
import de.dopichaj.labrador.search.hit.HitComparator;
import de.dopichaj.labrador.search.hit.MemoryHit;
import de.dopichaj.labrador.search.merge.pattern.AbstractPattern;
import de.dopichaj.labrador.search.merge.pattern.ScoreBooster;

import junit.framework.TestCase;

public abstract class PatternTestCase extends TestCase {

    protected static final class OutputMembership {
        public final double output;
        public final double membership;
        
        public OutputMembership(double output, double membership) {
            this.output = output;
            this.membership = membership;
        }
    }

    protected static final class StoringBooster implements ScoreBooster {

        protected Map<Hit, OutputMembership> resultMap = new HashMap<Hit, OutputMembership>();

        public void boostScore(Hit hit, double output, double membership) {
            resultMap.put(hit, new OutputMembership(output, membership));
        }

        public void boostScores(Collection<Hit> hits, double output, double membership) {
            for (Hit hit : hits) {
                boostScore(hit, output, membership);
            }
        }

        public void boostScoreUpwards(Hit hit, double output, double membership) {
            // do nothing, not needed
        }
    }
    
    protected StoringBooster booster;
    protected AbstractPattern pattern;

    private boolean patternHasBeenApplied;
    private static final File PATH = new File("dummy-path");
    private Hit parent;
    private SortedSet<Hit> children;
    
    @Override
    public void setUp() {
        booster = new StoringBooster();
        parent = null;
        children = new TreeSet<Hit>(new HitComparator());
        patternHasBeenApplied = false;
    }
    
    protected void makeParent(String xPath, int length, int position, float score) {
        parent = new MemoryHit(PATH, xPath, length, position, score);
    }

    protected void addChild(String xPath, int length, int position, float score) {
        assert parent != null;
        
        children.add(new MemoryHit(PATH, parent.getXPath() + xPath, length, position, score));
    }

    protected void applyPattern() {
        pattern.setBooster(booster);
        pattern.applyTo(parent, children);
        patternHasBeenApplied = true;
    }
    
    protected void assertNoBoost() {
        assertTrue("Pattern has not been applied in test", patternHasBeenApplied);
        for (Map.Entry<Hit, OutputMembership> entry : booster.resultMap.entrySet()) {
            OutputMembership om = entry.getValue();
            assertEquals("High membership for " + entry.getKey(), 0.0, om.membership, 0.001);
        }
    }
    
    private void assertHitBoost(Hit hit, double output, double membership) {
        assertTrue("Pattern has not been applied in test", patternHasBeenApplied);
        OutputMembership real = booster.resultMap.get(hit);
        assertEquals("Unexpected output", output, real.output, 0.001);
        assertEquals("Unexpected degree", membership, real.membership, 0.1);
    }
    
    protected void assertParentBoost(double output, double membership) {
        assertHitBoost(parent, output, membership);
    }
    
    /**
     * Assert that all children have an expected boost.
     */
    protected void assertChildrenBoost(double output, double membership) {
        for (Hit child : children) {
            assertHitBoost(child, output, membership);
        }
    }
}
